package lab2task1;

import java.util.ArrayList;
import java.util.List;

/**
 * 
 * @author Bulaev Igor
 *
 * Class that calculates progression sum in given number of threads
 *  without using java.util.concurrent library and synchronization
 *  blocks
 * 
 * @param sum - progression sum
 * @param threadCount - number of threads to be used
 * @param start - start value of progression
 * @param end - end value of progression
 *
 */

public class SinSumNoSyncBlocks {

	/**
	 * 
	 * Method that calculates progression sum
	 * 
	 * @param threadRange - range of values calculated in single thread
	 * 
	 */
	
	public double calculateSum(int threadCount, int start, int end) {
		if(start > end) {
			throw new IllegalArgumentException("start > end");
		}
		if(threadCount > Math.abs(start) + Math.abs(end)) {
			throw new IllegalArgumentException("Number of threads > number of values in progression");
		}		
		
		/** Get range of values for single thread by dividing sum of values by number of threads */
		int threadRange = ((Math.abs(start) + Math.abs(end)) / threadCount);
		List<WorkerThread> threads = new ArrayList<>();
		WorkerThread nextThread;		
		
		/** Create, launch threads */
		
		for (int i = 0; i < threadCount; i++) {
			int startPos = start + i * threadRange;
			if (i != threadCount - 1)
				nextThread = new WorkerThread(startPos, startPos + threadRange - 1);				
			else
				nextThread = new WorkerThread(startPos, end);
			nextThread.start();
			threads.add(nextThread);			
		}
		Double sum = 0d;
		for (WorkerThread cur : threads) {
			try {
				cur.join();				
				sum += cur.getSum();				
			} catch (InterruptedException ie) {
				ie.printStackTrace();
			}			
		}
		sum = (double)Math.round(sum * 1000000) / 1000000;
		return sum;
	}

	/**
	 * 
	 * Worker thread class
	 * 
	 * @param sum - sum calculated by this thread
	 * @param fromRange - lowest value in thread's range
	 * @param toRange - highest value in thread's range
	 *
	 */
	
	private class WorkerThread extends Thread {

		private double sum;
		private final int fromRange;
		private final int toRange;

		public WorkerThread(int fromRange, int toRange) {		
			this.fromRange = fromRange;
			this.toRange = toRange;			
		}

		public double getSum() {
			return sum;
		}

		@Override
		public void run() {
			for (int i = fromRange; i <= toRange; i++) {	
					sum += Math.sin(i);				
			}			
		}
	}
}
